package praktikum_I.blatt_I.aufgabe2;

import java.util.Collection;
import java.util.NoSuchElementException;

/**
 * Created with IntelliJ IDEA.
 * User: mizus
 * Date: 26.09.13
 * Time: 12:36
 * To change this template use File | Settings | File Templates.
 */
public class List_impl<E> implements List_inter<E> {


    private Node_inter<E> head;
    private int size = 0;
    private int referenzenCounter= 0;

    //////////       ////////
    //     Konstruktor     //
    /////////       /////////

    public List_impl(){
        head = new Node_Impl<E>();
    }

    /**
     *  Zuruecksetzen des Refrenzencounters auf Null
     */

    public void clearReferenzenCounter(){
        referenzenCounter = 0;
    }

    /**
     *  Gibt den Referenzencounter zurueck
     */

    public int getReferenzenCounter() {
        return referenzenCounter;
    }

    /**
     *  Zaehlt den Referenzencounter um 1 hoch
     */

    public void addReferenzenCounter(){
        referenzenCounter+=1;
    }


    /**
     * Fuegt elem in die Liste ein. Ist die Liste
     * leer, wird Elem automatisch der Kopf der Liste.
     * Besteht die Liste aus nur einem oder mehreren
     * Elementen, wird Elem an das letzte Element angehaengt
     * @param elem
     */

    @Override
    public void add(E elem){
        if(elem == null)
            throw new NoSuchElementException();
        Node_inter<E> toAdd = new Node_Impl<E>(elem, null);
        if (size() == 0) {
            this.head = toAdd;
            addReferenzenCounter();
        }
        else if (size() == 1) {
            this.head.setNext(toAdd);
            addReferenzenCounter();
        }
        else {
            Node_inter<E> tmp = this.head;
            for (int i =0 ; i< size()-1; i++){
                tmp = tmp.getNext();
                addReferenzenCounter();
            }
            tmp.setNext(toAdd);
        }
        size++;
    }

    private void out(String str){
        System.out.println(str);
    }

    /**
     * Haengt alle Elemente der collection der Reihe nach
     * an die Liste an
     * @param collection
     */

    @Override
    public void addAll(Collection<E> collection){
        for (E elem : collection){
            add(elem);
        }
    }


    /**
     *  Fuegt eine uebergebenes Element x als Kopf
     *  in die Liste selbst ein und gibt diese zurueck
     * @param x
     * @return List_inter
     */

    @Override
    public List_inter cons(E x) {
        if(this.isEmpty()) add(x);
        else{
            Node_inter newHead = new Node_Impl(x, this.head);
            this.head =newHead;
            addReferenzenCounter();

        }
        return this;
    }

    /**
     * Gibt die Laenge der Liste selbst zurueck
     * @return int
     */

    @Override
    public int length(){
        return size;
    }


    public int size() {
        int result = 0;
        if (this.head.getElem() == null) return result;
        else{
            return rek_size(result+=1, this.head);
        }
    }

    private int rek_size(int i, Node_inter<E> elem) {
        if(elem.getNext() == null){
            return i;
        } else {
            return rek_size(i+=1, elem.getNext());
        }
    }


    /**
     * Entfernt das erste Element der Liste selbst  und gibt das entfernte Elem zurueck
     * @return
     */

    @Override
    public Object head(){
        Node_inter result;
        if(isEmpty()) throw  new IndexOutOfBoundsException("Keine Elemente vorhanden");
        else{
            Node_inter next = this.head.getNext();
            result = this.head;
            this.head = next;
            size--;
            addReferenzenCounter();
        }
        return result;
    }



    /**
     * Prueft ob die Liste selbst leer ist
     * @return Boolean
     */

    @Override
    public boolean isEmpty() {
        addReferenzenCounter();
        return size() == 0;
    }


    /**
     * Fuegt ein uebergebenes Element x an der Position index
     * in der Liste selbst ein
     * @param x  einzufuegendes Element
     * @param index  an dieser Position einfuegen
     * @return List_inter
     */

    //ToDo

    @Override
    public List_inter insert(E x, int index){
        if(index > this.size() || index < 0) {
            throw new IndexOutOfBoundsException("Fehler bein Index");
        }
        else{
           if (index == 0) return cons(x);
           else {
              int count =0;
               return  rek_insert(new Node_Impl<E>(x),index,count, this.head);
           }
        }
    }

    private List_inter rek_insert(Node_inter<E> elem, int index, int count, Node_inter<E> currentNode) {
        if (count == index - 1) {
            elem.setNext(currentNode.getNext());
            currentNode.setNext(elem);
            return this;
        }
        return rek_insert(elem, index, count += 1, currentNode.getNext());
    }



    /**
     * Gibt den Kopf der Liste zurueck
     * @return E
     */

    @Override
    public E getHead() {
        addReferenzenCounter();
        return this.head.getElem();
    }

    @Override
    public int hashCode() {
        int result = head != null ? head.hashCode() : 0;
        result = 31 * result + size;
        return result;
    }

    /**
     * Strinrepaesentation der Liste mit Ausgabe des ReferenzenCounters
     * @return Stringrepreasentation auf der Konsole
     */

    public String toString(){
        String result = " { ";
        Node_inter<E> tmp = this.head;

        for (int i = 0; i < size; i++){
            result += tmp.toString() + ", ";
            tmp  = tmp.getNext();
            result+=" || " ;
        }
        result+= " }";
        return result;
    }

}


